
#include <dbg/ptest.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <compl.h>
#include <unix/page_alloc_os.h>
/* 测试 */
/* 配置 */
#include "test_common.h"

/* 保留一些内存 */

#define TEST_MINSIZE			(16)
#define TEST_MAXSIZE			(2048)

#define RESERVED_BLOCK_P		(64)

#define MAX_TEST_SIZE_PERTHREAD		(SIZE_4M) /* 每个申请 8M，保留一些 然后释放，每批 */


/* 限制开始申请的大小 */

#define THREAD_STARTNR(size)		((MAX_TEST_SIZE_PERTHREAD - MAX_BATCH_NR(size) * size) / size)

#define MAX_TEST_PER_BATCH(size)	(MAX_TEST_SIZE_PERTHREAD / size)

#define MAX_BATCH_NR(size) 		((TEST_NR / MAX_TEST_PER_BATCH(size)) + 1)

/* 16384次 */
// #define MAX_BATCH_NR(size) 		(MAX_TEST_SIZE_PERTHREAD / size)
// #define BATCH_STEP(size)		(2048 * size)
#define BATCH_STEP(size)		(1)
/* RECBATCH最大答案等于最大加batch数量 */
#define MAX_TEST_RECBATCH_NR(total_nr)	(total_nr + (MAX_BATCH_NR(TEST_MINSIZE) * TEST_THREADNR))


#define TEST_MAXTHREADS			(2 * HDEF_NATIVE_CPU_NR)
#define TEST_NR				(20000000 / TEST_THREADNR) /* 总共1M次，太少了? 先试试 */
/* 不打印 */
#define WCMF_PRINTF(...)		/* printf(__VA_ARGS__) */

// #define MAX_TEST_SIZE_PERTHREAD		((MAX_TEST_BATCH + MAX_BATCH_NR * TEST_THREADNR) * sizeof(dword_t))
#define TEST_THREADNR			(HDEF_NATIVE_CPU_NR)
#if (HDEF_NATIVE_CPU_NR == 8)
	#define WARMUP_THREAD_NR	(2 * HDEF_NATIVE_CPU_NR)
	// static word_t test_nrs_arr[] = {1, 2, 3, 4, 5, 6, 7, 8};
	static word_t test_nrs_arr[] = {8};
#elif (HDEF_NATIVE_CPU_NR == 16)
	#define WARMUP_THREAD_NR	(1)
	// static word_t test_nrs_arr[] = {1, 2, 4, 6, 8, 11, 13, 16};
	static word_t test_nrs_arr[] = {16};
#elif (HDEF_NATIVE_CPU_NR == 24)
	#define WARMUP_THREAD_NR	(1)
	// static word_t test_nrs_arr[] = {1, 2, 4, 8, 12, 16, 20, 24};
	static word_t test_nrs_arr[] = {24};
#endif

typedef struct bench_conf {
	size_t current_test_unitsize;
	size_t current_test_totalsz;
} bench_conf_t;

static recbatch_t *recs_arr[TEST_THREADNR + 1];
static bench_conf_t bench_conf;
static size_t total_exec_nr = 0;

static TLS_INITIAL cid_t mytid;

static cid_t enabled[TEST_THREADNR] = {0};
static word_t tlock = 0;
static void *bench_wcmf(void *args){
	PARAM_USED(args);
	static tstack_t reserved_list = {0};
	/* 根据核心ID制造不对称负载 */
	cid_t cid = mytid;
	/* 本次申请的负载 */
	size_t unit_size = bench_conf.current_test_unitsize;
	// size_t ori_max_cont = bench_conf.current_test_totalsz / unit_size;
	// size_t ori_max_cont = MAX_TEST_BATCH_RAND / TEST_THREADNR;
	// size_t ori_max_cont = (bench_conf.current_test_totalsz / unit_size) >> (cid >> 2);
	size_t ori_max_cont = bench_conf.current_test_totalsz / unit_size;
	size_t total_nr = ori_max_cont * TEST_THREADNR;

	// size_t real_max = bench_conf.current_test_totalsz / unit_size;
	// if (ori_max_cont > 100){ ori_max_cont = 100;	}
	// ori_max_cont = THREAD_STARTNR(unit_size) ;
	size_t max_cont = ori_max_cont;


	/* 申请队列 */
	recbatch_t *recbatch = recbatch_init(recs_arr[cid], recbatch_size(MAX_TEST_RECBATCH_NR(total_nr)), MAX_TEST_RECBATCH_NR(total_nr));
	
	size_t total = 0;
	size_t batch_incr = 0;
	ASSERT(unit_size >= sizeof(tsnode_t));
	size_t cnt = 0;
	while (total < TEST_NR){
		cnt = 0;
		/* 等待标记 */
		while (atm_ldacq(&enabled[cid]) == (cid_t)-1)
			PAUSE();

		/* 同步 */
		while (atm_ldacq(&tlock) == TEST_THREADNR){
			sched_yield();
  		}

		/* 申请一个批次 */
		for (word_t idx = 0; idx < max_cont + batch_incr; idx++){
			void *mem = malloc(unit_size);
			ASSERT(mem);
			// __memset(mem, cid, unit_size);
			atm_st((word_t *)mem, cid);
			/* 保留一部分不释放 */
			recbatch_push_rec(recbatch, mem, unit_size);
			cnt++;
			total++;
		}
		ASSERT(recbatch->used_nr != 0);
		recbatch_shuffle(recbatch, total);
		// 申请后全部push到全局链表中，然后由一个线程释放，其他线程尝试申请
		tstack_push(&reserved_list, (tsnode_t *)&recbatch->node);

		ASSERT(recbatch);
		
		/* 批次数+1 */
		batch_incr += BATCH_STEP(unit_size);
		// dbg_printf("[%u]batch %lu, %lu\n", cid, batch_incr, total);
		/* 委任下一个申请线程 */
		cid_t next_tid = atm_ld(&enabled[cid]);
		atm_st(&enabled[cid], (cid_t)-1);
		faa_rel(&tlock, 1);
		/* 被选中的线程 */
		if (cid == (next_tid % TEST_THREADNR)) {
			/* 等待所有线程完成 */
			while (atm_ldacq(&tlock) < TEST_THREADNR)
			{
				PAUSE();
			}

			recbatch_t *batch = (recbatch_t *)tstack_pop(&reserved_list);
			while(batch){
				word_t used_nr = batch->used_nr;
				for (word_t idx = 0; idx < used_nr; idx++){
					/* 释放 */
					void *mem = recbatch_pop_rec(batch)->ptr;
					ASSERT(mem);
					/* 随机保留，是否需要??不需要，因为这只是测某种情况 */
					// if (UNLIKELY(idx % RESERVED_BLOCK_P == 0))
					// 	tstack_push(&reserved_list, (tsnode_t *)mem);
					// else
						free(mem);
					
				}
				batch = (recbatch_t *)tstack_pop(&reserved_list);
			}
			for (word_t th = 0; th < TEST_THREADNR; th++)
			{
				/* 让cid逐步推进 */
				enabled[th] = (cid + (TEST_THREADNR / 2) + 1);
			}

			/* 完成后通知其他线程 */
			atm_strel(&tlock, 0);
		}

	}

	recs_arr[cid] = recbatch;
	return NULL;
}

static void bench_wcmf_setparam(size_t unit_size, size_t total_sz){
	bench_conf.current_test_unitsize = unit_size;
	bench_conf.current_test_totalsz = total_sz;
	__memset(&enabled[0], 0, sizeof(enabled));
	atm_strel(&tlock, 0);
}

__PERF_PARALLEL_FUNC_BINDCORE_CUSTOM(bench_wcmf , mytid, TEST_NR, TEST_MAXTHREADS, HDEF_LITTLECORE_OFFST, total_exec_nr = 0, test_nrs_arr, WCMF_PRINTF)

/* perf测试 */
int main(){
	/* 申请队列 */
	/* 按最大算 */
	size_t total_nr = (MAX_TEST_SIZE_PERTHREAD / 16) * TEST_THREADNR;
	// for(word_t idx = 0; idx < TEST_THREADNR; idx++){
	// 	total_nr += (MAX_TEST_SIZE_PERTHREAD / 16) >> (idx >> 1);
	// }
	size_t memrec_maxnr = MAX_TEST_RECBATCH_NR(total_nr);
	for (word_t idx = 0; idx <= TEST_THREADNR; idx++){
		
		void *recbatch_mem = page_alloc(recbatch_size(memrec_maxnr));
		ASSERT(recbatch_mem);
		recs_arr[idx] = (recbatch_t *)recbatch_mem;
	}
	recbatch_t *recbatch = recbatch_init(recs_arr[TEST_THREADNR], recbatch_size(memrec_maxnr), memrec_maxnr);
	/* 填入作为sentry */
	recbatch->node.next = NULL_PTR;
	tail->next = &recbatch->node;
	tail = &recbatch->node;
	// recbatch_exchange_locked(recbatch);
	
	/* 问题是从>256开始速度以1/2等比递减 */
	/* 问题解决方案1是增加slab大小 256 - 128KB 512 - 256KB 1024 - 512KB 2048 - 1MB 4096 - 2MB ... */
	/* 问题解决方案2是增加一层wblock层管理slab 每块大小4kb  */
	/* 设置参数 */
	// WCMF_PRINTF("[wcmf bench] start size = 16...");
	// bench_wcmf_setparam(16, MAX_TEST_SIZE_PERTHREAD);
	// PERF_PARALLEL_RUN(bench_wcmf);

	// WCMF_PRINTF("[wcmf bench] start size = 32...");
	// bench_wcmf_setparam(32, MAX_TEST_SIZE_PERTHREAD);
	// PERF_PARALLEL_RUN(bench_wcmf);

	// WCMF_PRINTF("[wcmf bench] start size = 48...");
	// bench_wcmf_setparam(48, MAX_TEST_SIZE_PERTHREAD);
	// PERF_PARALLEL_RUN(bench_wcmf);

	// WCMF_PRINTF("[wcmf bench] start size = 64...");
	// bench_wcmf_setparam(64, MAX_TEST_SIZE_PERTHREAD);
	// PERF_PARALLEL_RUN(bench_wcmf);

	// WCMF_PRINTF("[wcmf bench] start size = 96...");
	// bench_wcmf_setparam(96, MAX_TEST_SIZE_PERTHREAD);
	// PERF_PARALLEL_RUN(bench_wcmf);

	// WCMF_PRINTF("[wcmf bench] start size = 128...");
	// bench_wcmf_setparam(128, MAX_TEST_SIZE_PERTHREAD);
	// PERF_PARALLEL_RUN(bench_wcmf);

	// WCMF_PRINTF("[wcmf bench] start size = 192...");
	// bench_wcmf_setparam(192, MAX_TEST_SIZE_PERTHREAD);
	// PERF_PARALLEL_RUN(bench_wcmf);

	WCMF_PRINTF("[wcmf bench] start size = 256...");
	bench_wcmf_setparam(256, MAX_TEST_SIZE_PERTHREAD);
	PERF_PARALLEL_RUN(bench_wcmf);

	WCMF_PRINTF("[wcmf bench] start size = 384...");
	bench_wcmf_setparam(384, MAX_TEST_SIZE_PERTHREAD);
	PERF_PARALLEL_RUN(bench_wcmf);

	WCMF_PRINTF("[wcmf bench] start size = 512...");
	bench_wcmf_setparam(512, MAX_TEST_SIZE_PERTHREAD);
	PERF_PARALLEL_RUN(bench_wcmf);

	WCMF_PRINTF("[wcmf bench] start size = 768...");
	bench_wcmf_setparam(768, MAX_TEST_SIZE_PERTHREAD);
	PERF_PARALLEL_RUN(bench_wcmf);

	WCMF_PRINTF("[wcmf bench] start size = 1024...");
	bench_wcmf_setparam(1024, MAX_TEST_SIZE_PERTHREAD);
	PERF_PARALLEL_RUN(bench_wcmf);

	WCMF_PRINTF("[wcmf bench] start size = 1536...");
	bench_wcmf_setparam(1536, MAX_TEST_SIZE_PERTHREAD);
	PERF_PARALLEL_RUN(bench_wcmf);

	WCMF_PRINTF("[wcmf bench] start size = 2048...");
	bench_wcmf_setparam(2048, MAX_TEST_SIZE_PERTHREAD);
	PERF_PARALLEL_RUN(bench_wcmf);




	return 0;
}